using System;
using System.Collections.Generic;
using Infinity.Common.Extensions;

namespace Infinity.Common.Visitor
{
	/// <summary>
	/// General tree visitor
	/// </summary>
	public class TreeVisitor<TArg, TResult> : TreeVisitorBase<TArg, TResult>
		where TArg : class
	{
		/// <summary>
		/// It contains the visitor functions. 
		/// </summary>
		protected Dictionary<Type, Func<object, Func<TArg, TResult>, TResult>> m_TypeFuncDictionary { get; set; }

		/// <summary>
		/// Default constructor
		/// </summary>
		public TreeVisitor()
		{
			m_TypeFuncDictionary = new Dictionary<Type, Func<object, Func<TArg, TResult>, TResult>>();
		}

		/// <summary>
		/// It visits a given node.
		/// </summary>
		public override TResult Visit(TArg node)
		{
			if (node.IsNull())
				return default(TResult);

			var nodeType = node.GetType();

			Func<object, Func<TArg, TResult>, TResult> func;

			if (m_TypeFuncDictionary.TryGetValue(nodeType, out func))
				return func(node, Visit);

			throw new Exception(string.Format("Not supported node type: {0}!", nodeType));
		}

		/// <summary>
		/// It adds a new function for visiting a node
		/// </summary>
		public TreeVisitor<TArg, TResult> If<T>(Func<T, Func<TArg, TResult>, TResult> func)
		{
			m_TypeFuncDictionary.Add(typeof(T), (x, y) => func((T)x, y));
			return this;
		}

		/// <summary>
		/// It adds a new function for visiting a node
		/// </summary>
		public TreeVisitor<TArg, TResult> If<T>(Func<T, TResult> func)
		{
			m_TypeFuncDictionary.Add(typeof(T), (x, y) => func((T)x));
			return this;
		}
	}
}